package plp.functional2.expression;

import java.util.HashMap;
import java.util.Map;

import plp.expressions1.util.Tipo;
import plp.expressions2.expression.Expressao;
import plp.expressions2.expression.Valor;
import plp.expressions2.expression.ValorConcreto;
import plp.expressions2.memory.AmbienteCompilacao;
import plp.expressions2.memory.AmbienteExecucao;
import plp.expressions2.memory.VariavelJaDeclaradaException;
import plp.expressions2.memory.VariavelNaoDeclaradaException;
import plp.functional1.expression.TuplaException;

public class ValorMap extends ValorConcreto<HashMap<Expressao, Expressao>> {

	/**
	 * 
	 */

	private static final long serialVersionUID = 1L;

	public ValorMap(HashMap map) {
		super(map);
	}

	public Valor get(Valor key) throws MapException {
		Valor valor;
		try {
			valor = (Valor) valor().get(key);
		} catch (ClassCastException e) {
			throw new MapException(
					"A avaliacao da expressao precisa ser um valor.");
		}
		if (valor == null) {
			throw new MapException("Chave inexistente.");
		}
		return valor;
	}

	public Valor avaliar(AmbienteExecucao amb)
			throws VariavelNaoDeclaradaException, VariavelJaDeclaradaException,
			TuplaException, MapException {
		return this;
	}

	public boolean checaTipo(AmbienteCompilacao amb) {
		return true;
	}

	public Tipo getTipo(AmbienteCompilacao amb)
			throws VariavelNaoDeclaradaException, VariavelJaDeclaradaException {
		return Tipo.TIPO_MAP;
	}

	/**
	 * Retorna a aridade desta funcao.
	 * 
	 * @return a aridade desta funcao.
	 */
	public int getAridade() {
		return valor().size();
	}

	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("{");
		for (Map.Entry<Expressao, Expressao> mapeamento : valor().entrySet()) {
			Expressao key = mapeamento.getKey();
			Expressao value = mapeamento.getValue();
			sb.append(key + " : " + value + ", ");
		}
		sb.deleteCharAt(sb.length() - 2);
		sb.append("}");
		return sb.toString();
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof ValorMap) {

			if (((ValorMap) obj).valor().size() != this.valor().size()) {
				return false;
			}
			ValorMap valorMap = (ValorMap) obj;
			HashMap<Expressao, Expressao> aux1 = new HashMap<Expressao, Expressao>();
			HashMap<Expressao, Expressao> aux2 = new HashMap<Expressao, Expressao>();
			HashMap<Expressao, Expressao> aux3 = new HashMap<Expressao, Expressao>();

			for (Map.Entry<Expressao, Expressao> map1 : valor().entrySet()) {
				aux1.put(map1.getKey(), map1.getValue());
			}

			for (Map.Entry<Expressao, Expressao> map2 : valorMap.valor()
					.entrySet()) {
				aux2.put(map2.getKey(), map2.getValue());
			}

			boolean continua = true;

			for (Map.Entry<Expressao, Expressao> mp1 : aux1.entrySet()) {
				for (Map.Entry<Expressao, Expressao> mp2 : aux2.entrySet()) {
					if ((mp1.getKey().equals(mp2.getKey()) && (mp1.getValue()
							.equals(mp2.getValue())))) {
						aux3.put(mp1.getKey(), mp1.getValue());
					}

				}
			}
			if (aux3.size() == valor().size()) {
				return true;
			}
		}

		return false;
	}
}
